package com.ganwei.plugin;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.JarClassLoader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import java.io.File;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;


/**
 * @BelongsProject: plugin-demo-test
 * @BelongsPackage: com.plugin.demo
 * @Author: Owen
 * @CreateTime: 2022-12-27
 * @Description: 动态加载jar 加载类
 * @Version: 1.0
 */
@Slf4j
public class PluginImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    /**
     * jar包存放路径
     */
    private String libPath;


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取要动态加载的jar列表
        List<String> fileNames = FileUtil.listFileNames(libPath);
        if(CollUtil.isEmpty(fileNames)){
            log.info("热加载文件件中，文件为空 ---------------> {}", libPath);
            return;
        }
        List<String> jarNames = fileNames.stream().filter(jarName -> jarName.endsWith(".jar")).collect(Collectors.toList());
        if(CollUtil.isEmpty(jarNames)){
            log.info("热加载文件件中，jar文件为空 ---------------> {}", libPath);
            return;
        }
        //开始加载jar包
        try {
            //循环按顺序加载
            for (String jarName : jarNames) {
                log.info("开始热加载jar包 ---------------> {}", jarName);
                File jar = new File(libPath + "/" + jarName);
                if(!jar.exists()){
                    log.info("jar包不存在");
                }
                JarFile jarFile = new JarFile(jar);

                JarClassLoader jarClassLoader = JarClassLoader.loadJar(jar);

                for (Enumeration<JarEntry> ea = jarFile.entries(); ea.hasMoreElements(); ) {
                    JarEntry jarEntry = ea.nextElement();
                    String name = jarEntry.getName();
                    if (name.endsWith(".class")) {
                        String loadName = name.replace("/", ".").substring(0, name.length() - 6);
                        //加载class
                        Class<?> c = jarClassLoader.loadClass(loadName);
                        //注册bean
                        insertBean(c, registry);
                    }
                }
                log.info("结束热加载jar包 ---------------> {}", jarName);
            }
        } catch (Exception e) {
            log.error("热加载jar包异常");
            e.printStackTrace();
        }
    }


    private void insertBean(Class<?> c, BeanDefinitionRegistry registry) {
        if (isSpringBeanClass(c)) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(c);
            BeanDefinition beanDefinition = builder.getBeanDefinition();
            registry.registerBeanDefinition(c.getName(), beanDefinition);
        }
    }

    /**
     * 方法描述 判断class对象是否带有spring的注解
     *
     * @param cla jar中的每一个class
     * @return true 是spring bean   false 不是spring bean
     * @method isSpringBeanClass
     */
    public boolean isSpringBeanClass(Class<?> cla) {
        if (cla == null) {
            return false;
        }
        //是否是接口
        if (cla.isInterface()) {
            return false;
        }
        //是否是抽象类
        if (Modifier.isAbstract(cla.getModifiers())) {
            return false;
        }
        if (cla.getAnnotation(Component.class) != null) {
            return true;
        }
        if (cla.getAnnotation(Repository.class) != null) {
            return true;
        }
        if (cla.getAnnotation(Service.class) != null) {
            return true;
        }
        return false;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.libPath = environment.getProperty("libPath");
    }
}